package com.hsh.test.modules.interceptor;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections.MapUtils;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

@Intercepts({
	@Signature(type = StatementHandler.class, 
			method = "prepare", 
			args = {Connection.class,Integer.class}
	)
})
public class PaginationInterceptor implements Interceptor {
	
	private int DEFAULT_PAGE_SIZE = 10;//默认一页10条数据
	private int DEFAULT_PAGE_NUM = 1;//默认第一页
	
	private String page = "page"; //当前页面参数对应的key
    private String rows = "rows"; //pageSize对应的key
    
    public static final String COUNT_SQL_REG = "^SELECTCOUNT\\(";
    public static final String BLANK_REG = "\\s*|\\t|\\r|\\n|\\\\n";
    private static Pattern pattern = Pattern.compile(COUNT_SQL_REG, Pattern.CASE_INSENSITIVE);
    
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		if (!(invocation.getTarget() instanceof RoutingStatementHandler)) {
            return invocation.proceed();
        }
		RoutingStatementHandler handler = (RoutingStatementHandler) invocation.getTarget();
        BoundSql boundSql = handler.getBoundSql();
        Object param = boundSql.getParameterObject();
        if (param == null) {//无参数时不做分页处理
        	return invocation.proceed();  
        }
        Map<String, Object> pagination = this.parsePaginationParam(param);
        if(pagination == null || pagination.isEmpty()) {
        	return invocation.proceed();
        }else {
        	return this.processPagination(invocation, boundSql, pagination);
        }
	}
	/**
	 * 处理分页
	 * @param invocation
	 * @param boundSql
	 * @param param
	 * @return
	 * @throws Exception
	 */
	private Object processPagination(Invocation invocation, BoundSql boundSql, Map<String, Object> param) throws Exception {
        String paginationSql = this.buildPaginationSql(boundSql.getSql().trim(),param);
        try {
            Field field = boundSql.getClass().getDeclaredField("sql");
            field.setAccessible(true);
            field.set(boundSql, paginationSql);
            Object result = invocation.proceed();
            return result;
        }catch (Exception e) {
            throw new Exception("myBatis执行SQL失败!(" + paginationSql + ")", e);
        }
    }
	/**
     * 构建分页查询SQL
     *
     * @param sql
     * @param param
     * @return
     */
    private String buildPaginationSql(String sql, Map<String, Object> param) {
    	Integer start = MapUtils.getInteger(param, "page");
    	Integer limit = MapUtils.getInteger(param, "rows");
        String checkSql = sql.replaceAll(BLANK_REG, "");
        Matcher matcher = pattern.matcher(checkSql);
        if(matcher.find()){//排除sql count查询
            return sql;
        }else {
        	String paginationSql = sql;
        	if(limit != null && start != null && start > 0 && limit > 0){
        		start = (start-1) * limit;
        		paginationSql = paginationSql + " limit " + start.toString() + "," + limit.toString();
        	}
            return paginationSql;
        }
    }
	/**
     * 处理分页参数page、rows
     * @param param
     * @return
     */
    private Map<String, Object> parsePaginationParam(Object param) {
    	Map<String, Object> pagination = null;
    	if(param instanceof Map) {//Map类型的参数
    		Map<String, Object> paramMap = (Map<String, Object>) param;
    		if(paramMap.containsKey(this.page) && paramMap.containsKey(this.rows)) {
    			pagination = new HashMap<String, Object>();
        		pagination.put(this.page, MapUtils.getInteger(paramMap, this.page, this.DEFAULT_PAGE_NUM));
                pagination.put(this.rows, MapUtils.getInteger(paramMap, this.rows, this.DEFAULT_PAGE_SIZE));
    		}
    		return pagination;
    	}else {//非Map类型的参数，作为PO对象处理
    		Class clazz = param.getClass();
    		try {
    			if(clazz != Object.class) {
    				 pagination = new HashMap<String, Object>();
    				 Field startField = clazz.getDeclaredField(this.page);
                     startField.setAccessible(true);
                     Field limitField = clazz.getDeclaredField(this.rows);
                     limitField.setAccessible(true);
                     pagination.put(this.page, (Integer) startField.get(param));
                     pagination.put(this.rows, (Integer) limitField.get(param));
    			}
    		}catch (Exception e) {
    			pagination = null;
			}
    		return pagination;
    	}
    }
	
    @Override
	public Object plugin(Object target) {
		if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
	}

	@Override
	public void setProperties(Properties properties) {
		 this.page = properties.getProperty("page", "page");
	     this.rows = properties.getProperty("rows", "rows");		
	}

}
